﻿// libeventDemo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
//#include "event2/event-config.h"
#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <unistd.h>
#endif


#include "event2/event.h"
#include "event2/util.h"
#include "event2/event_compat.h"
#include "event2/event_struct.h"
#include "event2/bufferevent.h"
#include "event2/thread.h"

#include <event2/buffer.h>
#include <event2/listener.h>
#include <thread>

#include <atomic>
static std::atomic<int>  g_sendcount;

/*
* bufferevent_write: 发送缓存，基本不会失败，只保存到内存等待发送。先缓存到系统（socket缓存)->再保存到evbuff 中
* 回掉在event_loop 中执行，造成没有真正执行，bufferevent_write 只是保存到缓存
* 在停止接收的情况下，远端端口，也不会收到端口事件
* 主动关闭bufferevent_free，不会收到事件
* readcb 在有数据到达后产生每次接收到数据都产生一次回掉，保存到evbffer中等待用户读取
*/

void bufferevent_readcb_cb(struct bufferevent* bev, void* ctx)
{
	unsigned char tmp[128] = {0};
	struct event_base* base = bufferevent_get_base(bev);

	bufferevent_read(bev, tmp, 128);
	printf("cli:recv,count:%d,data:%s\n", g_sendcount++, tmp);

	//bufferevent_free(bev);

}
void bufferevent_event_cb2(struct bufferevent* bev, short what, void* ctx)
{
	printf("cli:bufferevent_event_cb2:%X\n", what);
	if (what & BEV_EVENT_CONNECTED) {
		/*const char *tmp = "123456789";

		int sendlen = bufferevent_write(bev, tmp, strlen(tmp));
		printf("connect async ok,sendlen:%d\n", sendlen);

		struct evbuffer *sendbuf = bufferevent_get_output(bev);
		int cachelen = evbuffer_get_length(sendbuf);
		printf("send cache len:%d\n", cachelen);*/

	}
	else if (what & BEV_EVENT_ERROR) {
		printf("cli:Client socket got error!\n");
	}

	
}
struct event_base* g_base_20220315 = nullptr;
static void tcpwork_thread() 
{
	event_base_loop(g_base_20220315, EVLOOP_NO_EXIT_ON_EMPTY);
	/* Dispatch */
	/*while (1) {
		int i = event_base_dispatch(base);

		printf("\nevent_base_dispatch=%d\n", i);
		std::this_thread::sleep_for(std::chrono::seconds(2));
	}*/
}

#include <vector>
static void tcpsend_thread() 
{
	struct sockaddr_in sin = { 0 };
	sin.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr);
	sin.sin_port = htons(8001);

	//创建socket
	//evutil_socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
	//设置地址复用和非阻塞
	//evutil_make_listen_socket_reuseable(sock);
	//evutil_make_socket_nonblocking(sock);
	struct bufferevent* psendEv = nullptr;
	for (int i = 0; i < 1; i++) {
		psendEv = bufferevent_socket_new(g_base_20220315, -1, BEV_OPT_CLOSE_ON_FREE /*| BEV_OPT_THREADSAFE*/);
		if (!psendEv) {
			printf("bufferevent_socket_new error\n");
		}
		bufferevent_setcb(psendEv, bufferevent_readcb_cb, nullptr, bufferevent_event_cb2, 0);
		bufferevent_enable(psendEv, EV_READ | EV_WRITE);
		int ret = bufferevent_socket_connect(psendEv, (struct sockaddr*)&sin, sizeof(sin));
		if (-1 == ret) {
			fprintf(stderr, "connect sync failed:%d\n", 0);
			break;
		}

		//const char* tmp = "123456789";
		//int sendlen = bufferevent_write(psendEv, tmp, strlen(tmp));
		// 
		
		std::this_thread::sleep_for(std::chrono::milliseconds(100));

		//bufferevent_free(psendEv);
	}

	//发送或连续发送，
	int len = 0;
	char* tmp = new char[1024];
	memset(tmp, 0, 1024);
	strcpy(tmp, "123456789");

	while (1) {
		int sendlen = bufferevent_write(psendEv, tmp, 1024);
		if (sendlen < 0) {
			printf("cli: send error\n");
			break;
		}
		struct evbuffer* sendbuf = bufferevent_get_output(psendEv);
		int cachelen = evbuffer_get_length(sendbuf);
		printf("cli: send cache len:%d\n", cachelen);
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	}

	bufferevent_free(psendEv);
}

//buffevent 测试
//libevent 支持多线程
//参考  https://blog.csdn.net/Rong_Toa/article/details/108853178
//libevent test-fdleak.c
int tcpClient_main()
{
	g_sendcount = 0;
//#ifdef _WIN32
	WSADATA wsa_data;
	WSAStartup(0x0201, &wsa_data);

	evthread_use_windows_threads();
//#endif
	g_base_20220315 = event_base_new();
	if (!g_base_20220315) {
		fprintf(stderr, "Could not initialize libevent!\n");
		return 1;
	}
	//evthread_make_base_notifiable(g_base_20220315);  //linux 下考虑调用这个。具体没有分析
	std::thread(tcpwork_thread).detach();

	for (int i = 0; i < 1; i++) {
		std::thread(tcpsend_thread).detach();
	}
	
	getchar();
	return 0;
}
